﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Vds2465Telegrams
{
	/// <summary>
	/// The UserDataColl class manages the user data elements that are included in a telegram.
	/// The following constraints have to be observed (page 50)
	/// 1. Each user data type must be included in the telegram only once
	/// 2. Only one report user data type must be included in the telegram (02H, 04H, 06H, 08H, 30H, 35H)
	/// 3. The user datas are ordered by their number ascendingly
	/// 4. There must not be too many user data elements so that the maximum allowed length is not exceeded
	/// </summary>
	public class UserDataColl
	{
		#region constants

		public const int MaxLength = 0xF7; // 247

		#endregion constants

		#region private member variables

		private readonly SortedList<byte, UserData> _userDataSortedList;

		private bool _reportUserDataTypeAdded = false;
		private int _currentLengthOfUserDataBytes = 0;

		#endregion private member variables

		#region constructor

		public UserDataColl()
		{
			_userDataSortedList = new SortedList<byte, UserData>();
		}

		#endregion constructor

		#region collection methods
				
		public bool Add(UserData userData)
		{
			// check the constraints

			// check if element is already in the list
			if (_userDataSortedList.ContainsKey(userData.DataType))
			{
				// already in list
				return false;
			}

			bool thisIsAReportUserDataType = UserDataColl_Helper.IsThisAReportUserDataType(userData.DataType);
			if (thisIsAReportUserDataType)
			{
				if (_reportUserDataTypeAdded)
				{
					// we already have a report user data type, only one is allowed
					return false;
				}
			}

			int lengthOfNewUserDataBytes = UserDataColl_Helper.LengthOfUserDataType(userData);
			if (_currentLengthOfUserDataBytes + lengthOfNewUserDataBytes > MaxLength)
			{
				// we are exceeding the length
				return false;
			}			

			// constraints have been satisfied, now actually do it...
			if (thisIsAReportUserDataType)
			{
				_reportUserDataTypeAdded = true;
			}
			_currentLengthOfUserDataBytes += lengthOfNewUserDataBytes;
			_userDataSortedList.Add(userData.DataType, userData);

			return true;
		}

		public int Count
		{
			get { return _userDataSortedList.Count; }
		}

		public bool GetByIndex(int index, out UserData userData)
		{
			userData = null;
			if (index < _userDataSortedList.Count)
			{
				IList<UserData> userDataColl = _userDataSortedList.Values;
				userData = userDataColl[index];
				return true;
			}

			return false;
		}

		public IEnumerator<UserData> GetEnumerator()
		{
			IList<UserData> userDataColl =_userDataSortedList.Values;
			return userDataColl.GetEnumerator();
		}

		#endregion collection methods

		#region ToString, Equals, GetHashCode

		public override int GetHashCode()
		{
			int hashCode = _userDataSortedList.Count;

			foreach(KeyValuePair<byte,UserData> userDataPair in _userDataSortedList)
			{
				hashCode ^= userDataPair.Key;			
			}

			return hashCode;
		}

		public override string ToString()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("(");

			if (_userDataSortedList.Count > 0)
			{
				IList<UserData> userDataColl = _userDataSortedList.Values;

				for (int index = 0; index < userDataColl.Count - 1; index++)
				{
					UserData userData = userDataColl[index];
					stringBuilder.Append(userData.ToString());
					stringBuilder.Append(", ");
				}
				UserData tmpUserDataLast = userDataColl[userDataColl.Count - 1];
				stringBuilder.Append(tmpUserDataLast.ToString());
			}

			stringBuilder.Append(")");

			return stringBuilder.ToString();
		}		

		public override bool Equals(object obj)
		{
			Type objType = obj.GetType();
			if (this.GetType() == objType)
			{
				UserDataColl userDataColl2Compare = obj as UserDataColl;
				if (this.Count == userDataColl2Compare.Count)
				{
					IList<UserData> userDataColl = _userDataSortedList.Values;

					// we can rely on the fact that the order is guarantueed
					for (int index = 0; index < userDataColl.Count; index++)
					{
						bool bRetVal;
						UserData userDataOwn = userDataColl[index];
						UserData userData2Compare;
						bRetVal = userDataColl2Compare.GetByIndex(index, out userData2Compare);
						if (bRetVal == false)
						{
							return false;
						}

						if (userDataOwn.Equals(userData2Compare) == false)
						{
							return false;
						}
					}

					return true;
				}

				// nr of elements differs
				return false;
			}

			// type differs
			return false;
		}

		#endregion ToString, Equals, GetHashCode		
	}
}
